package textcvt;

import java.util.List;

import textcvt.RuleScript.Matcher;
import textcvt.RuleScript.Pattern;
import textcvt.RuleScript.ScriptNode;

/**
 * 
 * @author Bing
 *
 */
public class SimpleRuleBasedFilter {	
	protected Pattern beginPattern = null;
	protected Pattern endPattern = null;
	protected FilterAction beginFilterAction = null;
	protected FilterAction endFilterAction = null;
	protected Pattern[] filterPatterns = null;	
	protected FilterAction[] filterActions = null;
	protected CharSequence textInput = null;
	/**-1:reach the end; other: index*/
	protected int next = 0;
	private StringBuilder out = new StringBuilder();
	
	public SimpleRuleBasedFilter(CharSequence text, RuleScript ruleScript){
		ScriptNode beginNode = ruleScript.getBegin();
		ScriptNode endNode = ruleScript.getEnd();
		String beginRule = null;
		String endRule = null;
		
		if(beginNode!=null){
			beginRule = beginNode.getNodeValue();
			ScriptNode action = ruleScript.getAction(beginNode.getAttribute(ScriptNode.ATTR_ACTION));
			if(action!=null){
				this.beginFilterAction = new FilterAction(); 
				this.beginFilterAction.ret = action.getNodeValue();
			}
		}
		if(endNode!=null){
			endRule = endNode.getNodeValue(); 
			ScriptNode action = ruleScript.getAction(endNode.getAttribute(ScriptNode.ATTR_ACTION));
			if(action!=null){
				this.endFilterAction = new FilterAction(); 
				this.endFilterAction.ret = action.getNodeValue();
			}
		}
		List<ScriptNode> filterRules = ruleScript.getFilters();
		
		this.textInput = text;
		
		if(beginRule!=null && !beginRule.isEmpty())
			this.beginPattern = Pattern.compile(beginRule, Pattern.DOTALL|Pattern.CASE_INSENSITIVE);
		
		if(endRule!=null && !endRule.isEmpty())
			this.endPattern = Pattern.compile(endRule,Pattern.DOTALL|Pattern.CASE_INSENSITIVE);
		
		int filter_count = filterRules ==null? 0: filterRules.size();
		this.filterPatterns = new Pattern[filter_count];
		this.filterActions = new FilterAction[filter_count];
		for(int i=0;i<filter_count;i++){
			this.filterPatterns[i] = Pattern.compile(filterRules.get(i).getNodeValue(),Pattern.DOTALL|Pattern.CASE_INSENSITIVE);
			this.filterActions[i] = new FilterAction();
			ScriptNode action = ruleScript.getAction(filterRules.get(i).getAttribute(ScriptNode.ATTR_ACTION));
			this.filterActions[i].ret = action==null?null:action.getNodeValue();
		}
	}	
	
	public CharSequence filter(){
		int[] region={0,0}; 
		
		Matcher matcher = null;	
		//
		// begin pattern
		//
		if(this.beginPattern!=null){			
			matcher = this.beginPattern.matcher(textInput);
		}
		if(matcher!=null && matcher.find()){
			region[0] = matcher.start();
			region[1] = matcher.end();
		}
		
		onBegin(textInput, region[0], region[1]);
				
		next = region[1];
		//
		// filter pattern & end pattern
		//		
		for(; next!=-1; next++){	
			if(-1==findNext(region))
				break;
			output(textInput,next,next);
		}

		return out;
	}
	
	protected void onBegin(CharSequence text, int s, int e){
		String rpl = this.beginFilterAction==null?null:this.beginFilterAction.actionPerformed(null);
		if(rpl!=null){
			output(rpl, 0, rpl.length());
		}
	}
	
	protected void onEnd(CharSequence text, int s, int e){	
		String rpl = this.endFilterAction==null?null:this.endFilterAction.actionPerformed(null);
		if(rpl!=null){
			output(rpl, 0, rpl.length());
		}
	}
	
	protected void onFilter(int filter_index, CharSequence text, int s, int e){	
		String rpl = this.filterActions[filter_index].actionPerformed(null);
		if(rpl!=null){
			output(rpl, 0, rpl.length());
		}
	}

	
	private int findNext(int[] region){		
		int matchEndIndex = next;
		boolean refind = true;
		while(refind){
			refind = false;			
			//check the end
			if(next>=this.textInput.length()){
				onEnd(textInput, next, -1);
				next = -1;
				return -1;
			}
			if(this.endPattern!=null && (matchEndIndex=nextMatch(this.textInput,next, this.endPattern))!=-1){
				onEnd(textInput, next, matchEndIndex);
				next = -1;
				return -1;
			}
			
			for(int i=0; i<this.filterPatterns.length; i++){			
				matchEndIndex = nextMatch(this.textInput,next, this.filterPatterns[i]);
				if(matchEndIndex != -1){
					onFilter(i, textInput, next, matchEndIndex);
					next = matchEndIndex;
					refind = true;
					break;
				}
			}
		}
		
		return next;
	}	
	
	/**
	 * s maybe equals with e
	 * @param s
	 * @param e
	 */
	protected void output(CharSequence text, int s, int e){
		if(e==-1)
			return;
		if(s==e)
			out.append(text.charAt(s));
		else
			out.append(text, s, e);
	}

	/**
	 * get the match end index
	 * @param text
	 * @param offset
	 * @param p
	 * @return -1: not match; other match end index
	 */
	private int nextMatch(CharSequence text, int offset, Pattern p){
//		Matcher m = p.matcher(text);
//		m.region(offset, text.length());	
//		return m.lookingAt() ? m.end() : -1;
		return p.matcher(text, offset);
	}
	
	protected static class FilterAction {
		String ret = null;
		public String actionPerformed(Object arg){
			return this.ret;
		}
	}
}
